#include "A_Config.h"
#include <base64_utils.h>
class AppDebugger : public AppBase
{
private:
    /* data */
    uint8_t count = 0;

public:
    AppDebugger()
    {
        name = "debugger";
        title = "开发者选项";
        description = "开发人员用到的工具，外加一些奇怪的功能";
        image = "\xEF\x97\xBC";
    }
    void setup();
    void loop();
    void destruct();
};

static lv_obj_t *lbl_sysinfo;
static lv_obj_t *btn_trySDmount;
static lv_obj_t *btn_trySDrw;
static lv_obj_t *btn_getIP;
static lv_obj_t *btn_inputIP;
static lv_obj_t *btn_connectWiFi;
static lv_obj_t *btn_tryTCP;
static lv_obj_t *btn_cmd;
static lv_obj_t *lbl_ipAddr;
static lv_obj_t *lbl_myipAddr;
static lv_obj_t *kbd_pinyin;
static bool testReq = false;
static bool ipreq = false;
static bool ipinreq = false;
static bool tcpreq = false;
static bool cmdreq = false;
static bool wifireq = false;
static bool musicdownloadlocalreq = false;
static bool sleepenreq = false;
static char strInfo[300];
static char ipaddr[50] = {0};
static bool receivedip;

static void IP_RECV_CB(const uint8_t *pkt, uint8_t size, uint16_t from)
{
    strncpy(ipaddr, (const char *)pkt, 50);
    receivedip = true;
}
static void update_myip()
{
    LOCKLV();
    lv_label_set_text(lbl_ipAddr, ipaddr);
    String myip = "MyIP:" + WiFi.localIP().toString();
    lv_label_set_text(lbl_myipAddr, myip.c_str());
    UNLOCKLV();
    if (strcmp(ipaddr, "") != 0)
    {
        settings.setChar("serverIP", ipaddr);
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////                                文件下载                                            ////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static lv_obj_t *window_progress;
static lv_obj_t *bar_progress;

String urlToDown;
bool downloadSuccessful = false;
static File *f_download;
SemaphoreHandle_t semaphoreDownload;

static void create_progress_bar()
{
    window_progress = lv_obj_create(lv_layer_top());
    lv_obj_set_size(window_progress, 240, 60);
    lv_obj_center(window_progress);
    bar_progress = lv_bar_create(window_progress);
    lv_obj_set_size(bar_progress, 200, 10);
    lv_obj_align(bar_progress, LV_ALIGN_BOTTOM_MID, 0, 0);
    lv_bar_set_value(bar_progress, 0, LV_ANIM_OFF);
    lv_obj_t *lbl_progress = lv_label_create(window_progress);
    lv_label_set_text(lbl_progress, "Downloading...");
    lv_obj_align(lbl_progress, LV_ALIGN_TOP_MID, 0, 0);
    lv_obj_pop_up(window_progress, 24, 300, 0);
}

static void del_progress_bar()
{
    lv_obj_fall_down(window_progress, 24, 250, 0);
    lv_obj_del_delayed(window_progress, 260);
}

static void task_music_downloader(void *)
{
    xSemaphoreTake(semaphoreDownload, portMAX_DELAY);
    HTTPClient http;
    http.begin(urlToDown);
    int httpCode = http.GET();
    if (httpCode == HTTP_CODE_OK)
    {
        // get length of document (is -1 when Server sends no Content-Length header)
        int len = http.getSize();
        if (len == -1)
            goto err;
        // create buffer for read
        uint8_t *buff;
        buff = (uint8_t *)ps_malloc(10240);
        // get tcp stream
        WiFiClient *stream = http.getStreamPtr();
        stream->setTimeout(5);
        // read all data from server
        size_t progLast = 0;
        size_t sizeSaved = 0;
        while (http.connected() && (sizeSaved != len))
        {
            // get available data size
            size_t size = stream->available();
            if (size)
            {
                int c = stream->readBytes(buff, len - sizeSaved > 10240 ? 10240 : len - sizeSaved);
                f_download->write(buff, c);
                sizeSaved += c;
            }
            else
            {
                delay(1);
                continue;
            }
            size_t progNow = sizeSaved * 100;
            progNow /= len;
            if (progLast != progNow)
            {
                progLast = progNow;
                lv_bar_set_value(bar_progress, progNow, LV_ANIM_ON);
                delay(10);
            }
        }
        free(buff);
    }
    else
        goto err;

    http.end();
    f_download->close();
    downloadSuccessful = true;
    xSemaphoreGive(semaphoreDownload);
    vTaskDelete(NULL);
    return;
err:
{
    String pathto = f_download->path();
    f_download->close();
    SDCARD.remove(pathto);
    http.end();
    downloadSuccessful = false;
    xSemaphoreGive(semaphoreDownload);
    vTaskDelete(NULL);
}
}
static void download(String url, File *file)
{
    create_progress_bar();
    downloadSuccessful = false;
    urlToDown = url;
    f_download = file;
    semaphoreDownload = xSemaphoreCreateMutex();
    xTaskCreatePinnedToCore(task_music_downloader, "MusicDownload", 8192, NULL, 1, NULL, 1);
    delay(100);
    xSemaphoreTake(semaphoreDownload, portMAX_DELAY);
    xSemaphoreGive(semaphoreDownload);
    del_progress_bar();
    delay(100);
    vSemaphoreDelete(semaphoreDownload);
    if (downloadSuccessful)
        lv_toast("下载成功");
    else
        lv_toast("下载失败");
}

static bool download_music_local(String keyword)
{
    bool getRandom = false;
    String choiceID;
    String result;
    HTTPClient http;
    DynamicJsonDocument doc(10240);
    String url = "https://" CLOUDMUSIC_DOMAIN "/search?limit=3&keywords=";
    if (keyword == "")
    {
        url = "https://api.uomg.com/api/rand.music?sort=热歌榜&format=json";
        getRandom = true;
    }
    else
    {
        url += urlcode.urlencode(keyword);
    }
    http.begin(url);
    int code = http.GET();
    if (code != 200)
    {
        lv_toast("错误：无法连接HTTPS服务器");
        Serial.printf("HTTP CODE: %d\n", code);
        http.end();
        return false;
    }
    result = http.getString();
    deserializeJson(doc, result);

    if (getRandom == false)
    {
        uint16_t offset = 0;
        while (1)
        {
            if (doc["code"] == 200)
            {
                int ids[3];
                uint8_t i;
                String songNames[3];
                menu_create();
                for (i = 0; i < 3; ++i)
                {
                    ids[i] = doc["result"]["songs"][i]["id"];
                    if (ids[i] == 0)
                        break;
                    songNames[i] = doc["result"]["songs"][i]["name"].as<String>() + String("-") + doc["result"]["songs"][i]["ar"][0]["name"].as<String>();
                    menu_add(songNames[i].c_str());
                }
                menu_add(LV_SYMBOL_NEXT " 下一页");
                doc.clear();
                uint8_t choice = menu_show();
                if (choice == 0)
                {
                    lv_toast("已取消");
                    http.end();
                    return false;
                }
                else if (choice == i + 1) // 如果是下一页
                {
                    offset += 3;
                    String url_suffix = "&offset=" + String(offset);
                    result = "";
                    doc.clear();
                    http.begin(url + url_suffix);
                    int code = http.GET();
                    if (code != 200)
                    {
                        lv_toast("错误：无法连接HTTPS服务器");
                        Serial.printf("HTTP CODE: %d\n", code);
                        http.end();
                        return false;
                    }
                    result = http.getString();
                    deserializeJson(doc, result);
                    continue;
                }
                choiceID = String(ids[choice - 1]);
                keyword = songNames[choice - 1];
                break;
            }
            else
            {
                lv_toast("搜索：JSON不符合预期");
                http.end();
                return false;
            }
        }
    }
    else
    {
        if (doc["code"] == 1)
        {
            url = doc["data"]["url"].as<String>();
            int first_id = url.indexOf("id=") + 3;
            choiceID = url.substring(first_id, url.indexOf("\"", first_id));
            keyword = doc["data"]["name"].as<String>() + String("-") + doc["data"]["artistsname"].as<String>();
        }
        else
        {
            lv_toast("随机歌曲获取失败");
            http.end();
            return false;
        }
    }
    url = "https://" CLOUDMUSIC_DOMAIN "/song/url?id=";
    url += String(choiceID);
    http.begin(url);
    if (http.GET() != 200)
    {
        lv_toast("获取歌曲下载地址失败");
        http.end();
        return false;
    }
    result = http.getString();
    deserializeJson(doc, result);
    if (doc["code"] == 200)
    {
        url = doc["data"][0]["url"].as<String>();
        if (msgbox_yn((String("是否下载：") + keyword).c_str()) == true)
        {
            File fmusic = SDCARD.open(String("/m/") + keyword + String(".mp3"), "w");
            if (!fmusic)
            {
                lv_toast("无法创建文件");
                http.end();
                return false;
            }
            download(url, &fmusic);
        }
    }
    else
    {
        lv_toast("下载：JSON不符合预期");
        http.end();
        return false;
    }
    http.end();
    return true;
}

void AppDebugger::setup()
{
    LOCKLV();
    lv_obj_align(label(scr, "开发者模式", 0, 0), LV_ALIGN_TOP_MID, 0, 0);
    btn_trySDmount = button(scr, "测试最新功能", 0, 20, &testReq);
    lbl_myipAddr = label(scr, "本机IP地址", 0, 60);
    lbl_ipAddr = label(scr, "对方IP地址", 0, 80);
    btn_getIP = button(scr, "获取IP", 0, 100, &ipreq);
    btn_inputIP = button(scr, "输入IP", 80, 100, &ipinreq);
    btn_connectWiFi = button(scr, "连接WiFi", 0, 140, &wifireq);
    btn_tryTCP = button(scr, "连接TCP", 100, 140, &tcpreq);
    btn_cmd = button(scr, "通过TCP执行指令", 0, 180, &cmdreq);
    button(scr, "音乐下载器", 0, 220, &musicdownloadlocalreq);
    button(scr, "禁用保持唤醒", 150, 220, &sleepenreq);
    lbl_sysinfo = label(scr, "系统信息", 160, 20);
    UNLOCKLV();
    hal.axpShortPress = false;
    String last_ip = settings.getChar("serverIP", "");
    strcpy(ipaddr, last_ip.c_str());
    update_myip();
}

void AppDebugger::loop()
{
    count = count + 1;
    if (count >= 10)
    {
        count = 0;
        sprintf(strInfo, "硬件信息\n堆内存:%u KiB\nOSPI内存:%u KiB\n运行时间:%u s\nCPU频率:%u MHz\nFlash频率:%u MHz\nFlash容量:%u MiB\n", heap_caps_get_free_size(MALLOC_CAP_INTERNAL) / 1024,
                ESP.getFreePsram() / 1024,
                millis() / 1000, ESP.getCpuFreqMHz(), ESP.getFlashChipSpeed() / 1000000, ESP.getFlashChipSize() / 1024 / 1024);
        lv_label_set_text(lbl_sysinfo, strInfo);
    }
    if (hal.axpShortPress)
    {
        hal.axpShortPress = false;
        schoolWiFi.client.stop();
        WiFi.mode(WIFI_OFF);
        // xSemaphoreTake(hal.SPILock, portMAX_DELAY);
        // xSemaphoreGive(hal.SPILock);
        hal.DoNotSleep = false;
        appManager.goBack();
        return;
    }
    if (testReq)
    {
        testReq = false;
        if (WiFiMgr.autoConnect() == false)
            return;
        String v = "{\"method\":\"bilibili\",\"payload\":\"";
        v += msgbox_string("请输入av或bv号");
        v += "\"}";
        if (schoolWiFi.connectTCP())
        {
            schoolWiFi.client.write(v.c_str());
            String res = schoolWiFi.client.readStringUntil('\n');
            if (res.indexOf("true") != -1)
            {
                // 成功打开服务
                schoolWiFi.client.stop();
                lv_toast("服务端已启动，正在连接...");
                delay(2000);
                schoolWiFi.client.connect(HOME_TCP_VIDEO_HOST, HOME_TCP_VIDEO_PORT);
                if (schoolWiFi.client.connected())
                {
                    videoPlayer.play();
                }
                schoolWiFi.client.stop();
            }
            else
            {
                Serial.println(res);
            }
        }
    }
    if (ipreq)
    {
        ipreq = false;
        receivedip = false;
        ESPNow.start();
        bool b = ESPNow.sendCmd(CMD_GETIP, "");
        if (b == false)
        {
            msgbox("错误", "接收IP地址失败-服务器不在范围内");
            ESPNow.RecvCB = NULL;
            ESPNow.end();
            return;
        }
        uint8_t i = 0;
        ESPNow.RecvCB = IP_RECV_CB;
        while (receivedip == false)
        {
            delay(10);
            ++i;
            if (i > 100)
            {
                msgbox("错误", "接收IP地址失败-指令未收到回复（对方已接收）");
                ESPNow.RecvCB = NULL;
                ESPNow.end();
                return;
            }
        }
        ESPNow.RecvCB = NULL;
        ESPNow.end();
        update_myip();
    }
    if (wifireq)
    {
        wifireq = false;
        hal.DoNotSleep = true;
        WiFiMgr.connectGUI();
        update_myip();
    }
    if (tcpreq)
    {
        tcpreq = false;
        if (WiFi.isConnected())
        {
            schoolWiFi.client.setTimeout(2);
            schoolWiFi.client.connect(ipaddr, HOME_TCP_PORT, 2000);
            if (schoolWiFi.client.connected())
            {
                msgbox("提示", "TCP服务连接成功！");
                settings.writeSettings(CONFIG_FILE_NAME);
            }
            else
            {
                msgbox("错误", "无法连接TCP服务");
            }
        }
        schoolWiFi.client.stop();
    }
    if (cmdreq)
    {
        cmdreq = false;
        if (WiFi.isConnected())
        {
            if (schoolWiFi.client.connected() == false)
            {
                schoolWiFi.client.connect(ipaddr, HOME_TCP_PORT, 2000);
                if (schoolWiFi.client.connected() == false)
                    return;
            }
            String cmd = msgbox_string("请输入cmd指令", false);
            String out = "";
            DynamicJsonDocument doc(1024);
            doc["method"] = "cmd";
            doc["payload"] = cmd;
            serializeJson(doc, out);
            if (schoolWiFi.client.connected() == false)
            {
                schoolWiFi.client.connect(ipaddr, HOME_TCP_PORT, 2000);
                if (schoolWiFi.client.connected() == false)
                    return;
            }
            schoolWiFi.client.write(out.c_str());
            schoolWiFi.client.flush();
            schoolWiFi.client.stop();
            msgbox("提示", "指令已发送");
        }
    }
    if (ipinreq)
    {
        ipinreq = false;
        String s = msgbox_string("请输入IP地址或主机名", false);
        strncpy(ipaddr, s.c_str(), 50);
        lv_label_set_text(lbl_ipAddr, ipaddr);
    }
    if (musicdownloadlocalreq)
    {
        if (WiFi.isConnected() == false)
        {
            if (WiFiMgr.autoConnect() == false)
                return;
        }
        hal.DoNotSleep = true;
        download_music_local(msgbox_string("搜索歌曲", false, false, true));
        musicdownloadlocalreq = false;
    }
    if (sleepenreq)
    {
        lv_toast("已经允许自动休眠，即使WiFi已连接");
        hal.DoNotSleep = false;
        sleepenreq = false;
    }
}

void AppDebugger::destruct()
{
}

static AppDebugger app;